#include "QmlVirtualKeyboard.h"
#include <QApplication>
#include <QDebug>
#include <QWidget>
#include <QLineEdit>
#include <QInputMethodEvent>
#include "xyvirtualkeyboard.h"

/**
 * @brief QmlVirtualKeyboard::QmlVirtualKeyboard
 * 注意：
 * Mainwindow
 *   --centerwidget
 *       --otherwidget
 * parent必须为Mainwindow，其他widget必须以centerwidget为父对象且与centerwidget同大小，否则会出错
 * 因为键盘会移动centerwidget达到适配的目的
 * @param parent
 * @param qml
 */
QmlVirtualKeyboard::QmlVirtualKeyboard(QWidget *parent):QObject(parent){
    if(parent){
        m_kbd = XYVirtualKeyboard::getInstance();
        m_kbd->setParent(parent);
        m_kbd->setFixedSize(parent->width(),parent->height()/2);
        m_kbd->move(0,parent->height()/2);
        m_kbd->setFocusPolicy(Qt::NoFocus);
        establishConnection();
        m_kbd->hide();
        bindingInputMethod();
    }
}

QmlVirtualKeyboard::~QmlVirtualKeyboard(){
    delete m_kbd;
    m_kbd = nullptr;
    m_obj = nullptr;
}

/**
 * @brief QmlVirtualKeyboard::show
 * @param now
 */
void QmlVirtualKeyboard::show(QLineEdit *now){
    if(m_kbd&&!m_kbd->isVisible()){
        m_kbd->setVisible(true);
        qApp->inputMethod()->setVisible(true);
        recoverObjWidget();
        moveObjWidget(now);
    }
}

/**
 * @brief QmlVirtualKeyboard::hide
 */
void QmlVirtualKeyboard::hide(){
    XYVirtualKeyboard::getInstance()->enter_clicked();
    if(m_kbd&&m_kbd->isVisible()){
        m_kbd->setVisible(false);
        recoverObjWidget();
        qApp->inputMethod()->setVisible(false);
    }
}

/**
 * @brief 移动目标窗口 使edit不会被键盘遮住
 * @param edit
 */
void QmlVirtualKeyboard::moveObjWidget(QLineEdit* edit){
    if(!edit)return;
    QPoint bleft = edit->parentWidget()->mapTo(m_kbd->parentWidget(),edit->geometry().bottomLeft());
    if(bleft.y()>m_kbd->y()){
        QObject * p = edit->parent();
        while(p->parent() != m_kbd->parent()){
            p = p->parent();
        }
        m_obj = dynamic_cast<QWidget*>(p);
        m_obj->move(0,-(bleft.y()-m_kbd->y()));
    }else{
        m_obj = nullptr;
    }
}

/**
 * @brief 处理焦点改变
 * @param _now
 */
void QmlVirtualKeyboard::onFocusChanged(QWidget *_old, QWidget *_now){
    QLineEdit *now = dynamic_cast<QLineEdit *>(_now);
    if(_old) {
        XYVirtualKeyboard::getInstance()->enter_clicked(_old);
    }
    now?show(now):hide();
}

/**
 * @brief 连接信号
 */
void QmlVirtualKeyboard::establishConnection(){
    QObject::connect(qApp,&QApplication::focusChanged,
                     this,&QmlVirtualKeyboard::onFocusChanged);

    connect(qApp->inputMethod(),
            &QInputMethod::visibleChanged,
            this,&QmlVirtualKeyboard::onVisibleChanged);
}

/**
 * @brief 恢复被移动过的界面
 */
void QmlVirtualKeyboard::recoverObjWidget(){
    if(m_obj){
        if(qApp->focusWidget()){
            qApp->focusWidget()->clearFocus();
        }
        m_obj->move(0,0);
        m_obj = nullptr;
    }
}

/**
 * @brief 处理键盘发过来的隐藏信号
 */
void QmlVirtualKeyboard::onRectangleHide(){
    hide();
}

void QmlVirtualKeyboard::onVisibleChanged(){
    if(qApp->inputMethod()->isVisible()){
        show(dynamic_cast<QLineEdit *>(qApp->focusWidget()));
    }else{
        hide();
    }
}


void QmlVirtualKeyboard::bindingInputMethod() {

    XYVirtualKeyboard::getInstance()->initPinyinDictionary();
    QObject::connect(XYVirtualKeyboard::getInstance(),
                     &XYVirtualKeyboard::send_preedit, [](const QString &text){
        QInputMethodEvent *event =
                new QInputMethodEvent(text, QList<QInputMethodEvent::Attribute>());

        QWidget *focusWindow = qApp->focusWidget();
        if (focusWindow) {
            qApp->postEvent(focusWindow, event);
        }
    });
    QObject::connect(XYVirtualKeyboard::getInstance(),
                     &XYVirtualKeyboard::send_commit, [](const QString &text){
        QInputMethodEvent *event = new QInputMethodEvent;
        event->setCommitString(text);
        QWidget *focusWindow = qApp->focusWidget();
        if (focusWindow) {
            qApp->postEvent(focusWindow, event);
        }
    });

    QObject::connect(XYVirtualKeyboard::getInstance(),
                     &XYVirtualKeyboard::keyClicked, [](int unicode, int key, Qt::KeyboardModifiers modifiers, bool press){
        QKeyEvent *event = new QKeyEvent(press ? QEvent::KeyPress : QEvent::KeyRelease,
                                         key,
                                         modifiers,
                                         QChar(unicode));

        QWidget *focusWindow = qApp->focusWidget();
        if (focusWindow) {
            qApp->postEvent(focusWindow, event);
        }
    });
}
